﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;

using DataTransferObject;
using DDay.iCal.Components;
using DDay.iCal;
using DDay.iCal.DataTypes;


namespace Engine.ScheduleAlgo
{
    /*
     * Nhiệm vụ cao cả là chạy 1 thuật toán tính ra calendar
     * Đầu vào là danh sách task đã có độ ưu tiên
     */
    /// <summary>
    /// 
    /// </summary>
    public class SortingTaskAlgorithm
    {

        #region Essential Methods


        /// <summary>
        /// Sorts the task beta.
        /// </summary>
        /// <param name="taskList">The task list.</param>
        /// <param name="freeTimeList">The free time list.</param>
        /// <param name="systemConfiguration">The system configuration.</param>
        /// <param name="iCal">The i cal.</param>
        /// <param name="report">The report.</param>
        /// <returns></returns>
        public static List<Event> sortTask(TaskList taskList, FreeTimeList freeTimeList,
            SystemConfiguration systemConfiguration, iCalendar iCal, ref ScheduleReport report)
        {
            /**
             * trong phiên bản này, mặc định là freeTimeList là 1 List các FreeTimeBlock vô tận.
             * Bởi vì sắp xếp các task trải dài ra trong tương lai
             */
            // Tạo list các events
            List<Event> events = new List<Event>();
            int i = 0;//task đầu tiên
            FreeTimeBlock currentBlock = freeTimeList.nextBlock();//FreeTimeblock đầu tiên
            Task currentTask;
            while (i < taskList.NumberOfTask)
            {
                currentTask = taskList.GetTaskAt(i);
                int compare = currentTask.CompareWithTimeBlock(currentBlock);// gọi hàm so sánh task với freetimeblock
                if (compare == Task.SMALLER || compare == Task.FIT)
                {
                    Event newEvent = iCal.Create<Event>();// Tạo event 
                    
                    newEvent.Start = new iCalDateTime(currentBlock.StartDateTime.Year, currentBlock.StartDateTime.Month, currentBlock.StartDateTime.Day
                        , currentBlock.StartDateTime.Hour, currentBlock.StartDateTime.Minute, currentBlock.StartDateTime.Second);
                    
                    DateTime endEvent = currentBlock.StartDateTime.AddHours(currentTask.RemainTime);
                    newEvent.DTEnd = new iCalDateTime(endEvent.Year, endEvent.Month, endEvent.Day
                        , endEvent.Hour, endEvent.Minute, endEvent.Second);
                    // phải set Summary cho event , nếu set Name cho event thì event sẽ không hiện
                    // trong calendar
                    newEvent.Summary = currentTask.Name;                    
                    if (compare == Task.SMALLER)
                    {
                        currentBlock.StartDateTime = (currentBlock.StartDateTime.AddHours(currentTask.TotalWork));
                    }
                    else if (compare == Task.FIT)
                    {
                        currentBlock = freeTimeList.nextBlock();
                    }
                    currentTask.Completed = true;
                    currentTask.RemainTime = 0;
                    events.Add(newEvent);
                }
                else if (compare == Task.LARGER)
                {
                    Event newEvent = iCal.Create<Event>();// Tạo event 
                    newEvent.DTStart = new DDay.iCal.DataTypes.iCalDateTime(currentBlock.StartDateTime);
                    newEvent.DTEnd = new DDay.iCal.DataTypes.iCalDateTime(currentBlock.EndDateTime);
                    newEvent.Name = currentTask.Name;
                    currentTask.RemainTime = currentTask.RemainTime - (int)currentBlock.getTimeSpan().TotalHours;
                    currentBlock = freeTimeList.nextBlock();
                    events.Add(newEvent);
                }

                if (currentTask.Completed)
                {
                    i++;
                }
            }
            return events;
        }



        public static List<Task> sortTaskBeta(TaskList taskList, FreeTimeList freeTimeList,
            SystemConfiguration systemConfiguration, iCalendar iCal, ref ScheduleReport report)
        {
            #region Giới thiệu về giải thuật
            /**
             * Giải thuật sử dụng heuristic dựa trên 2 giải thuật academic là giải thuật qiải quyết bài tóan 1 | intree | sum (W(i) * C(i))
             * là bài tóan tới ưu Weighted Completiong time và giải thuật 1 || sum (U(i)) là bài tóan tối ưu số job trễ.
             * Cả hai bài tóan trên đều được giải với thời gian polynomial, vì vậy chúng ta có thể kết hợp giải thuật của 2 bài tóan
             * trên để tạo ra 1 giải thuật có thể chấp nhận dc
             */ 
            #endregion

            double currenttime = 0;
            PriorityQueue Jc = new PriorityQueue();
            ArrayList Jd = new ArrayList();
            List<Task> originalListOFTask = taskList.GetTaskList;
            ArrayList Jlist = new ArrayList();
            ArrayList result = new ArrayList();

            #region Bước 1: khởi tạo JList
            foreach (Task t in originalListOFTask)
            {
                Jlist.Add(new WrappedTaskCompareByDueDate(t, Jlist));
            }
            // JList chỉ được add thêm vào, ko dc remove vì preced cons của wrappedTask sẽ phụ thuộc
            // vào index trên JList
            #endregion
            


            while (Jlist.Count != 0)
            {
                #region Bước 2 : lấy ra các task có thể thực thi vào thời điểm t và bỏ vào Jc
                foreach (WrappedTaskCompareByDueDate t in Jlist)
                {
                    if (t.IsAvailableAt(currenttime))
                    {
                        Jc.Enqueue(t);
                        t.IsChosen = true;
                    }
                }
                #endregion

                #region Bước 3
                #region Bước 3.1: từ Jc chọn ra task có DueDate nhỏ nhất và bỏ vào vị trí currentTime của result
                WrappedTaskCompareByDueDate chosenTask = Jc.Dequeue() as WrappedTaskCompareByDueDate;
                chosenTask.IsScheduled = true;
                result.Add(chosenTask);
                chosenTask.StartTime = (chosenTask.ReleaseTime <= currenttime) ? currenttime : chosenTask.ReleaseTime;
                #endregion

                #region Bước 3.2: thiết lập currentTime
                currenttime = chosenTask.StartTime + chosenTask.ProcessingTime;
                #endregion

                #region Bước 3.3: nếu task j bị trễ, duyệt trong result lấy ra task max (p / w) và bỏ vào tập Jd
                if (currenttime > chosenTask.DueDate) // task mới thêm vào bị trễ
                {
                    // chọn task để bỏ vào Jd là task có max (p/w)
                    double max = 0;
                    double maxTaskPosition = 0;
                    WrappedTaskCompareByDueDate removedTask = null;
                    for (int i = 0; i < result.Count; i++)
                    {
                        WrappedTaskCompareByDueDate currentTask = result[i] as WrappedTaskCompareByDueDate;
                        if (((double)currentTask.ProcessingTime / (double)currentTask.Weight) > max)
                        {
                            max = ((double)currentTask.ProcessingTime / (double)currentTask.Weight);
                            maxTaskPosition = i;
                            removedTask = currentTask;
                        }
                    }
                    result.Remove(removedTask);
                    Jd.Add(removedTask);
                }
                #endregion

                #endregion
            }

            #region Bước 4: sửng dụng heuristic chọn những task trong Jd có thể bỏ vào những idle time block trong result

            #endregion

            #region Bước 5: nối result và Jd lại ra kết quả cuối cùng
            result.AddRange(Jd);
            #endregion
            return new List<Task>(result.ToArray(typeof(Task)) as Task[]);
        }
        
        #endregion
    }
}
